/* 
 * Copyright (C) 2015 fede
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package Control;

import Entity.WeatherForecast;
import java.io.IOException;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import net.aksingh.owmjapis.HourlyForecast;
import net.aksingh.owmjapis.OpenWeatherMap;

import org.json.JSONException;

/**
 *
 * @author fede
 */
@Stateless
public class WeatherForecastFacade extends AbstractFacade<WeatherForecast> {

    @PersistenceContext(unitName = "meteocal")
    private EntityManager em;

    /**
     * get entity manager
     *
     * @return entity manager of the class
     */
    @Override
    protected EntityManager getEntityManager() {
        return em;
    }

    /**
     * This method get weather using openweathermap API for specified location
     * and date
     *
     * @param location to check
     * @param date to check
     * @return the Id of weather forecast in the DB
     */
    public Long getWeather(String location, Date date) {
        if (date.after(GregorianCalendar.getInstance().getTime())) {
            OpenWeatherMap owm = new OpenWeatherMap("");

            HourlyForecast fwd;
            try {
                fwd = owm.hourlyForecastByCityName(location);

                if (fwd.hasForecastCount()) {
                    WeatherForecast weatherForecast = new WeatherForecast();
                    boolean trovato = false;
                    for (int i = 0; i < fwd.getForecastCount(); i++) {
                        HourlyForecast.Forecast forecast = fwd.getForecastInstance(i);
                        if (forecast.getDateTime().after(date) && trovato == false) {
                            trovato = true;
                            for (int j = 0; j < forecast.getWeatherCount(); j++) {
                                HourlyForecast.Forecast.Weather weather = forecast.getWeatherInstance(j);
                                if (weather.hasWeatherIconName() && !Float.isNaN(forecast.getMainInstance().getTemperature()) && weather.hasWeatherDescription()) {
                                    weatherForecast.setIcon(weather.getWeatherIconName());
                                    weatherForecast.setTemperature((forecast.getMainInstance().getTemperature() - 32) / (float) 1.8);
                                    weatherForecast.setDescription(weather.getWeatherDescription());
                                    weatherForecast.setDescriptionId(weather.getWeatherCode());
                                    em.persist(weatherForecast);
                                    em.flush();
                                    return weatherForecast.getId();
                                }
                            }
                        }
                    }
                }
            } catch (IOException | JSONException ex) {
                Logger.getLogger(WeatherForecastFacade.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return (long) -1;

    }

    /**
     * This method update weather using openweathermap API for specified
     * location and date
     *
     * @param weatherForecast to update
     * @param location to check
     * @param date to check
     */
    public void updateWeather(WeatherForecast weatherForecast, String location, Date date) {

        if (weatherForecast == null) {
            getWeather(location, date);
        } else {
            OpenWeatherMap owm = new OpenWeatherMap("");

            HourlyForecast fwd;
            try {
                fwd = owm.hourlyForecastByCityName(location);

                if (fwd.hasForecastCount()) {
                    boolean trovato = false;
                    for (int i = 0; i < fwd.getForecastCount(); i++) {
                        HourlyForecast.Forecast forecast = fwd.getForecastInstance(i);
                        if (forecast.getDateTime().after(date) && trovato == false) {
                            trovato = true;
                            for (int j = 0; j < forecast.getWeatherCount(); j++) {
                                HourlyForecast.Forecast.Weather weather = forecast.getWeatherInstance(j);
                                if (weather.hasWeatherIconName() && !Float.isNaN(forecast.getMainInstance().getTemperature()) && weather.hasWeatherDescription()) {
                                    weatherForecast.setIcon(weather.getWeatherIconName());
                                    weatherForecast.setTemperature((forecast.getMainInstance().getTemperature() - 32) / (float) 1.8);
                                    weatherForecast.setDescription(weather.getWeatherDescription());
                                    weatherForecast.setDescriptionId(weather.getWeatherCode());
                                    em.merge(weatherForecast);
                                    em.flush();

                                }

                            }

                        }
                    }
                }
            } catch (IOException | JSONException ex) {
                Logger.getLogger(WeatherForecastFacade.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }

    /**
     * this method get the description of WeatherForecast
     *
     * @param id of weather forecast
     * @return String of description
     */
    public String getDescription(Long id) {
        return em.find(WeatherForecast.class, id).getDescription();
    }

    /**
     * this method get the icon of WeatherForecast
     *
     * @param id of weather forecast
     * @return String of icon
     */
    public String getIcon(Long id) {
        return em.find(WeatherForecast.class, id).getIcon();
    }

    /**
     * this method get the temperature of WeatherForecast
     *
     * @param id of weather forecast
     * @return Float of temperature
     */
    public Float getTemperature(Long id) {
        return em.find(WeatherForecast.class, id).getTemperature();
    }

    /**
     * find weatherforecast by Id
     *
     * @param id to find
     * @return Weather Forecast find
     */
    public WeatherForecast getForecast(Long id) {

        return em.find(WeatherForecast.class, id);
    }

    /**
     * This method give the closest sunny day of specified date and location
     *
     * @param date to check
     * @param location to check
     * @return Date of closest sunny day
     */
    public Date closestSunnyDay(Date date, String location) {

        OpenWeatherMap owm = new OpenWeatherMap("");

        HourlyForecast fwd;
        try {
            fwd = owm.hourlyForecastByCityName(location);

            if (fwd.hasForecastCount()) {
                boolean trovato = false;
                for (int i = 0; i < fwd.getForecastCount(); i++) {
                    HourlyForecast.Forecast forecast = fwd.getForecastInstance(i);
                    if ((forecast.getWeatherInstance(0).getWeatherCode() == 800
                            || forecast.getWeatherInstance(0).getWeatherCode() == 801)
                            && trovato == false && forecast.getDateTime().after(date)) {
                        trovato = true;
                        for (int j = 0; j < forecast.getWeatherCount(); j++) {
                            HourlyForecast.Forecast.Weather weather = forecast.getWeatherInstance(j);
                            if (weather.hasWeatherIconName()
                                    && !Float.isNaN(forecast.getMainInstance().getTemperature())
                                    && weather.hasWeatherDescription()) {
                                return forecast.getDateTime();

                            }

                        }

                    }
                }
            }
        } catch (IOException | JSONException ex) {
            Logger.getLogger(WeatherForecastFacade.class.getName()).log(Level.SEVERE, null, ex);
        }
        return date;
    }

    /**
     * constructor
     */
    public WeatherForecastFacade() {
        super(WeatherForecast.class);
    }

}
